home *** CD-ROM | disk | FTP | other *** search
/ MACD 5 / MACD 5.bin / workbench / wb / czesc_2 / memmometer / src / minrexx.c < prev    next >
C/C++ Source or Header  |  1992-11-06  |  16KB  |  442 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.  This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  */
  21. static char *blurb = "Radical Eye MinRexx 0.4.AZ" ;
  22. /*
  23.  *   We read in our own personal little include.
  24.  */
  25. #include "minrexx.h"
  26. /*
  27.  *   All of our local globals, hidden from sight.
  28.  */
  29. static struct MsgPort *rexxPort ;          /* this is *our* rexx port */
  30. static int bringerdown ;                   /* are we trying to shut down? */
  31. static struct rexxCommandList *globalrcl ; /* our command association list */
  32. static long stillNeedReplies ;             /* how many replies are pending? */
  33. static long rexxPortBit ;                  /* what bit to wait on for Rexx? */
  34. static char *extension ;                   /* the extension for macros */
  35. static void (*userdisp)(struct RexxMsg *, struct rexxCommandList *, char *) ;
  36.                                            /* the user's dispatch function */
  37. static struct RexxMsg *oRexxMsg ;          /* the outstanding Rexx message */
  38. /*
  39.  *   Our library base.  Don't you dare close this!
  40.  */
  41. struct RxsLib *RexxSysBase ;
  42. /*
  43.  *   This is the main entry point into this code.
  44.  */
  45. long upRexxPort(
  46. /*
  47.  *   The first argument is the name of your port to be registered;
  48.  *   this will be used, for instance, with the `address' command of ARexx.
  49.  */
  50. char *s,
  51. /*
  52.  *   The second argument is an association list of command-name/user-data
  53.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  54.  *   structure with a NULL in the name field. The commands are case
  55.  *   sensitive.  The user-data field can contain anything appropriate,
  56.  *   perhaps a function to call or some other data.
  57.  */
  58. struct rexxCommandList *rcl,
  59. /*
  60.  *   The third argument is the file extension for ARexx macros invoked
  61.  *   by this program.  If you supply this argument, any `primitive' not
  62.  *   in the association list rcl will be sent out to ARexx for
  63.  *   interpretation, thus allowing macro programs to work just like
  64.  *   primitives.  If you do not want this behavior, supply a `NULL'
  65.  *   here, and those commands not understood will be replied with an
  66.  *   error value of RXERRORNOCMD.
  67.  */
  68. char *exten,
  69. /*
  70.  *   The fourth argument is the user dispatch function.  This function
  71.  *   will *only* be called from rexxDisp(), either from the user calling
  72.  *   this function directly, or from dnRexxPort().  Anytime a command
  73.  *   match is found in the association list, this user-supplied function
  74.  *   will be called with two arguments---the Rexx message that was
  75.  *   received, and a pointer to the association pair.  This function
  76.  *   should return a `1' if the message was replied to by the function
  77.  *   and a `0' if the default success code of (0, 0) should be returned.
  78.  *   Note that the user function should never ReplyMsg() the message;
  79.  *   instead he should indicate the return values with replyRexxCmd();
  80.  *   otherwise we lose track of the messages that still lack replies.
  81.  */
  82. void (*uf)(struct RexxMsg *, struct rexxCommandList *, char *))
  83. /*
  84.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  85.  *   If something goes wrong, it simply returns a `0'.  Note that this
  86.  *   function is safe to call multiple times because we check to make
  87.  *   sure we haven't opened already.  It's also a quick way to change
  88.  *   the association list or dispatch function.
  89.  */
  90. {
  91. /*
  92.  *   Some basic error checking.
  93.  */
  94.    if (rcl == NULL || uf == NULL)
  95.       return(0L) ;
  96. /* : ai=0 bk=0 ts=8 */
  97. /*
  98.  *   If we aren't open, we make sure no one else has opened a port with
  99.  *   this name already.  If that works, and the createport succeeds, we
  100.  *   fill rexxPortBit with the value to return.
  101.  *
  102.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  103.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  104.  */
  105.    if (rexxPort == NULL) {
  106.       Forbid() ;
  107.       if (FindPort((UBYTE *)s)==NULL)
  108.          rexxPort = CreatePort((UBYTE *)s, 0L) ;
  109.       Permit() ;
  110.       if (rexxPort != NULL)
  111.          rexxPortBit = 1L << rexxPort->mp_SigBit ;
  112.    }
  113. /*
  114.  *   Squirrel away these values for our own internal access, and return
  115.  *   the wait bit.
  116.  */
  117.    globalrcl = rcl ;
  118.    extension = exten ;
  119.    userdisp = uf ;
  120.    return(rexxPortBit) ;
  121. }
  122. /*
  123.  *   This function closes the rexx library, but only if it is open
  124.  *   and we aren't expecting further replies from REXX.  It's
  125.  *   *private*, but it doesn't have to be; it's pretty safe to
  126.  *   call anytime.
  127.  */
  128. static void closeRexxLib(void) {
  129.    if (stillNeedReplies == 0 && RexxSysBase) {
  130.       CloseLibrary((struct Library *)RexxSysBase) ;
  131.       RexxSysBase = NULL ;
  132.    }
  133. }
  134. /*
  135.  *   This function closes down the Rexx port.  It is always safe to
  136.  *   call, and should *definitely* be made a part of your cleanup
  137.  *   routine.  No arguments and no return.  It removes the Rexx port,
  138.  *   replies to all of the messages and insures that we get replies
  139.  *   to all the ones we sent out, closes the Rexx library, deletes the
  140.  *   port, clears a few flags, and leaves.
  141.  */
  142. void dnRexxPort(void) {
  143.    if (rexxPort) {
  144.       RemPort(rexxPort) ;
  145.       bringerdown = 1 ;
  146. /*
  147.  *   A message still hanging around?  We kill it off.
  148.  */
  149.       if (oRexxMsg) {
  150.          oRexxMsg->rm_Result1 = RXERRORIMGONE ;
  151.          ReplyMsg((struct Message *)oRexxMsg) ;
  152.          oRexxMsg = NULL ;
  153.       }
  154.       while (stillNeedReplies) {
  155.          WaitPort(rexxPort) ;
  156.          dispRexxPort() ;
  157.       }
  158.       closeRexxLib() ;
  159.       DeletePort(rexxPort) ;
  160.       rexxPort = NULL ;
  161.    }
  162.    rexxPortBit = 0 ;
  163. }
  164. /*
  165.  *   Here we dispatch any REXX messages that might be outstanding.
  166.  *   This is the main routine for handling Rexx messages.
  167.  *   This function is fast if no messages are outstanding, so it's
  168.  *   pretty safe to call fairly often.
  169.  *
  170.  *   If we are bring the system down and flushing messages, we reply
  171.  *   with a pretty serious return code RXERRORIMGONE.
  172.  *
  173.  *   No arguments, no returns.
  174.  */
  175. static int cmdcmp(char *, char *) ;
  176. static int dontreply ;
  177. void dispRexxPort(void) {
  178.    register struct RexxMsg *RexxMsg ;
  179.    register struct rexxCommandList *rcl ;
  180.    register char *p ;
  181.  
  182. /*
  183.  *   If there's no rexx port, we're out of here.
  184.  */
  185.    if (rexxPort == NULL)
  186.       return ;
  187. /*
  188.  *   Otherwise we have our normal loop on messages.
  189.  */
  190.    while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
  191. /*
  192.  *   If we have a reply to a message we sent, we look at the second
  193.  *   argument.  If it's set, it's a function we are supposed to call
  194.  *   so we call it.  Then, we kill the argstring and the message
  195.  *   itself, decrement the outstanding count, and attempt to close
  196.  *   down the Rexx library.  Note that this call only succeeds if
  197.  *   there are no outstanding messages.  Also, it's pretty quick, so
  198.  *   don't talk to me about efficiency.
  199.  */
  200.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  201.          if (RexxMsg->rm_Args[1])  {
  202.          ((int (*)(struct RexxMessage *))(RexxMsg->rm_Args[1]))(RexxMsg);
  203.          /* HOLY STRUCTURE DECLARATIONS, BATMAN! CAN YOU SAY "GO READ H&S"? */
  204.          }
  205.          DeleteArgstring(RexxMsg->rm_Args[0]) ;
  206.          DeleteRexxMsg(RexxMsg) ;
  207.          stillNeedReplies-- ;
  208.          closeRexxLib() ;
  209. /*
  210.  *   The default case is we got a message and we need to check it for
  211.  *   primitives.  We skip past any initial tabs or spaces and initialize
  212.  *   the return code fields.
  213.  */
  214.       } else {
  215.          p = (char *)RexxMsg->rm_Args[0] ;
  216.          while (*p > 0 && *p <= ' ')
  217.             p++ ;
  218.          RexxMsg->rm_Result1 = 0 ;
  219.          RexxMsg->rm_Result2 = 0 ;
  220. /*
  221.  *   If somehow the reply is already done or postponed, `dontreply' is
  222.  *   set.
  223.  */
  224.          dontreply = 0 ;
  225. /*
  226.  *   If the sky is falling, we just blow up and replymsg.
  227.  */
  228.          if (bringerdown) {
  229.             RexxMsg->rm_Result1 = RXERRORIMGONE ;
  230. /*
  231.  *   Otherwise we cdr down our association list, comparing commands,
  232.  *   until we get a match.  If we get a match, we call the dispatch
  233.  *   function with the appropriate arguments, and break out.
  234.  */
  235.          } else {
  236.             oRexxMsg = RexxMsg ;
  237.             for (rcl = globalrcl; rcl->name; rcl++) {
  238.                if (cmdcmp(rcl->name, p) == 0) {
  239.                   userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
  240.                   break ;
  241.                }
  242.             }
  243. /*
  244.  *   If we broke out, rcl will point to the command we executed; if we
  245.  *   are at the end of the list, we didn't understand the command.  In
  246.  *   this case, if we were supplied an extension in upRexxPort, we know
  247.  *   that we should send the command out, so we do so, synchronously.
  248.  *   The synchronous send takes care of our reply.  If we were given a
  249.  *   NULL extension, we bitch that the command didn't make sense to us.
  250.  */
  251.             if (rcl->name == NULL) {
  252.                if (extension) {
  253.                   syncRexxCmd((char *)RexxMsg->rm_Args[0], RexxMsg) ;
  254.                   dontreply = 1 ;
  255.                } else {
  256.                   RexxMsg->rm_Result1 = RXERRORNOCMD ;
  257.                }
  258.             }
  259.          }
  260. /*
  261.  *   Finally, reply if appropriate.
  262.  */
  263.          oRexxMsg = NULL ;
  264.          if (! dontreply)
  265.             ReplyMsg((struct Message *)RexxMsg) ;
  266.       }
  267.    }
  268. }
  269. /*
  270.  *   This is the function we use to see if the command matches
  271.  *   the command string.  Not case sensitive, and the real command only
  272.  *   need be a prefix of the command string.  Make sure all commands
  273.  *   are given in lower case!
  274.  */
  275. static int cmdcmp(char *c, char *m) {
  276.    while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
  277.       c++ ;
  278.       m++ ;
  279.    }
  280.    return((int)*c) ;
  281. }
  282. /*
  283.  *   Opens the Rexx library if unopened.  Returns success (1) or
  284.  *   failure (0).  This is another function that is *private* but
  285.  *   that doesn't have to be.
  286.  */
  287. static int openRexxLib(void) {
  288.    if (RexxSysBase)
  289.       return(1) ;
  290.    return((RexxSysBase = (struct RxsLib *)OpenLibrary((UBYTE *)RXSNAME, 0L)) != NULL) ;
  291. }
  292. /*
  293.  *   This is the general ARexx command interface, but is not the one
  294.  *   you will use most of the time; ones defined later are easier to
  295.  *   understand and use.  But they all go through here.
  296.  */
  297. struct RexxMsg *sendRexxCmd(
  298. char *s,
  299. /*
  300.  *   The first parameter is the command to send to Rexx.
  301.  */
  302. void (*f)(struct RexxMsg *),
  303. /*
  304.  *   The second parameter is either NULL, indicating that the command
  305.  *   should execute asynchronously, or a function to be called when the
  306.  *   message we build up and send out here finally returns.  Please note
  307.  *   that the function supplied here could be called during cleanup after
  308.  *   a fatal error, so make sure it is `safe'.  This function always is
  309.  *   passed one argument, the RexxMsg that is being replied.
  310.  */
  311. STRPTR p1, STRPTR p2, STRPTR p3)
  312. /*
  313.  *   These are up to three arguments to be stuffed into the RexxMsg we
  314.  *   are building up, making the values available when the message is
  315.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  316.  */
  317. {
  318.    struct RexxMsg *CreateRexxMsg() ;
  319.    STRPTR CreateArgstring() ;
  320.    register struct MsgPort *rexxport ;
  321.    register struct RexxMsg *RexxMsg ;
  322.  
  323. /*
  324.  *   If we have too many replies out there, we just return failure.
  325.  *   Note that you should check the return code to make sure your
  326.  *   message got out!  Then, we forbid, and make sure that:
  327.  *      - we have a rexx port open
  328.  *      - Rexx is out there
  329.  *      - the library is open
  330.  *      - we can create a message
  331.  *      - we can create an argstring
  332.  *
  333.  *   If all of these succeed, we stuff a few values and send the
  334.  *   message, permit, and return.
  335.  */
  336.    if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
  337.       return(NULL) ;
  338.    RexxMsg = NULL ;
  339.    if (openRexxLib() && (RexxMsg =
  340.        CreateRexxMsg(rexxPort,(UBYTE *)extension,
  341.         (UBYTE *)rexxPort->mp_Node.ln_Name)) &&
  342.          (RexxMsg->rm_Args[0] = CreateArgstring((UBYTE *)s, (long)strlen(s)))) {
  343.       RexxMsg->rm_Action = RXCOMM ;
  344.       RexxMsg->rm_Args[1] = (STRPTR)f ;
  345.       RexxMsg->rm_Args[2] = p1 ;
  346.       RexxMsg->rm_Args[3] = p2 ;
  347.       RexxMsg->rm_Args[4] = p3 ;
  348.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
  349.       Forbid() ;
  350.       if (rexxport = FindPort((UBYTE *)RXSDIR))
  351.          PutMsg(rexxport, (struct Message *)RexxMsg) ;
  352.       Permit() ;
  353.       if (rexxport) {
  354.          stillNeedReplies++ ;
  355.          return(RexxMsg) ;
  356.       } else
  357.          DeleteArgstring(RexxMsg->rm_Args[0]) ;
  358.    }
  359.    if (RexxMsg)
  360.       DeleteRexxMsg(RexxMsg) ;
  361.    closeRexxLib() ;
  362.    return(NULL) ;
  363. }
  364. /*
  365.  *   This function is used to send out an ARexx message and return
  366.  *   immediately.  Its single parameter is the command to send.
  367.  */
  368. struct RexxMsg *asyncRexxCmd(char *s) {
  369.    return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
  370. }
  371. /*
  372.  *   This function sets things up to reply to the message that caused
  373.  *   it when we get a reply to the message we are sending out here.
  374.  *   But first the function we pass in, which actually handles the reply.
  375.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  376.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  377.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  378.  *   along.
  379.  */
  380. static void replytoit(struct RexxMsg *msg) {
  381.    register struct RexxMsg *omsg ;
  382.  
  383.    omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
  384.    replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
  385.    ReplyMsg((struct Message *)omsg) ;
  386. }
  387. /*
  388.  *   This function makes use of everything we've put together so far,
  389.  *   and functions as a synchronous Rexx call; as soon as the macro
  390.  *   invoked here returns, we reply to `msg', passing the return codes
  391.  *   back.
  392.  */
  393. struct RexxMsg *syncRexxCmd(char *s, struct RexxMsg *msg) {
  394.    return(sendRexxCmd(s, replytoit, (STRPTR)msg, NULL, NULL)) ;
  395. }
  396. /*
  397.  *   There are times when you want to pass back return codes or a
  398.  *   return string; call this function when you want to do that,
  399.  *   and return `1' from the user dispatch function so the main
  400.  *   event loop doesn't reply (because we reply here.)  This function
  401.  *   always returns 1.
  402.  */
  403. void replyRexxCmd(
  404. /*
  405.  *   The first parameter is the message we are replying to.
  406.  */
  407. register struct RexxMsg *msg,
  408. /*
  409.  *   The next two parameters are the primary and secondary return
  410.  *   codes.
  411.  */
  412. register long primary, long secondary,
  413. /*
  414.  *   The final parameter is a return string.  This string is only
  415.  *   returned if the primary return code is 0, and a string was
  416.  *   requested.
  417.  *
  418.  *   We also note that we have replied to the message that came in.
  419.  */
  420. register char *string) {
  421. /*
  422.  *   Note how we make sure the Rexx Library is open before calling
  423.  *   CreateArgstring . . . and we close it down at the end, if possible.
  424.  */
  425.    if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
  426.       if (string && openRexxLib())
  427.          secondary = (long)CreateArgstring((UBYTE *)string,
  428.                                                           (long)strlen(string));
  429.       else
  430.          secondary = 0L ;
  431.    }
  432.    msg->rm_Result1 = primary ;
  433.    msg->rm_Result2 = secondary ;
  434.    closeRexxLib() ;
  435. }
  436. /*
  437.  *   If we don't want to reply, we call this.
  438.  */
  439. void DontReply(void) {
  440.    dontreply = 1 ;
  441. }
  442.